/*
 * Copyright (c) 2006-2007 Erin Catto http://www.gphysics.com
 *
 * This software is provided 'as-is', without any express or implied
 * warranty.  In no event will the authors be held liable for any damages
 * arising from the use of this software.
 * Permission is granted to anyone to use this software for any purpose,
 * including commercial applications, and to alter it and redistribute it
 * freely, subject to the following restrictions:
 * 1. The origin of this software must not be misrepresented; you must not
 * claim that you wrote the original software. If you use this software
 * in a product, an acknowledgment in the product documentation would be
 * appreciated but is not required.
 * 2. Altered source versions must be plainly marked as such, and must not be
 * misrepresented as being the original software.
 * 3. This notice may not be removed or altered from any source distribution.
 */

package org.box2dflash.collision.shapes {
	import org.box2dflash.collision.shapes.CircleShape;
	import org.box2dflash.collision.AABB;
	import org.box2dflash.common.math.*;
	import org.box2dflash.common.*;
	import org.box2dflash.dynamics.*;
	import org.box2dflash.collision.*;
	use namespace Internal;

	/// A shape is used for collision detection. Shapes are created in World.
	/// You can use shape for collision detection before they are attached to the world.
	/// @warning you cannot reuse shapes.
	public class Shape {
		/// Get the type of this shape. You can use this to down cast to the concrete shape.
		/// @return the shape type.
		public function get type():int {
			return m_type;
		}

		/// Is this shape a sensor (non-solid)?
		/// @return the true if the shape is a sensor.
		public function get isSensor():Boolean {
			return m_isSensor;
		}

		/// Set the contact filtering data. You must call World::Refilter to correct
		/// existing contacts/non-contacts.
		public function set filterData(filter:FilterData):void {
			m_filter = filter.copy;
		}

		/// Get the contact filtering data.
		public function get filterData():FilterData {
			return m_filter.copy;
		}

		/// Get the parent body of this shape. This is NULL if the shape is not attached.
		/// @return the parent body.
		public function get body():Body {
			return m_body;
		}

		/// Get the next shape in the parent body's shape list.
		/// @return the next shape.
		public function get next():Shape {
			return m_next;
		}

		/// Get the user data that was assigned in the shape definition. Use this to
		/// store your application specific data.
		public function get userData():* {
			return m_userData;
		}

		/// Set the user data. Use this to store your application specific data.
		public function set sserData(data:*):void {
			m_userData = data;
		}

		/// Test a point for containment in this shape. This only works for convex shapes.
		/// @param xf the shape world transform.
		/// @param p a point in world coordinates.
		public  function testPoint(xf:XForm, p:Vec2):Boolean {
			return false;
		};

		/// Perform a ray cast against this shape.
		/// @param xf the shape world transform.
		/// @param lambda returns the hit fraction. You can use this to  the contact point
		/// p = (1 - lambda) * segment.p1 + lambda * segment.p2.
		/// @param normal returns the normal at the contact point. If there is no intersection, the normal
		/// is not set.
		/// @param segment defines the begin and end point of the ray cast.
		/// @param maxLambda a number typically in the range [0,1].
		/// @return true if there was an intersection.
		public  function  testSegment(xf:XForm,
								lambda:Array, // float pointer
								normal:Vec2, // pointer
								segment:Segment,
								maxLambda:Number):Boolean {
			return false;
		};

		/// Given a transform, compute the associated axis aligned bounding box for this shape.
		/// @param aabb returns the axis aligned box.
		/// @param xf the world transform of the shape.
		public  function  computeAABB(aabb:AABB, xf:XForm):void {
		};

		/// Given two transforms, compute the associated swept axis aligned bounding box for this shape.
		/// @param aabb returns the axis aligned box.
		/// @param xf1 the starting shape world transform.
		/// @param xf2 the ending shape world transform.
		public  function  computeSweptAABB(	aabb:AABB,
									xf1:XForm,
									xf2:XForm):void {
		};

		/// Compute the mass properties of this shape using its dimensions and density.
		/// The inertia tensor is computed about the local origin, not the centroid.
		/// @param massData returns the mass data for this shape.
		public  function  computeMass(massData:MassData):void {
		};

		/// Get the maximum radius about the parent body's center of mass.
		public function get sweepRadius():Number {
			return m_sweepRadius;
		}

		/// Get the coefficient of friction.
		public function get friction():Number {
			return m_friction;
		}

		/// Get the coefficient of restitution.
		public function get restitution():Number {
			return m_restitution;
		}

		//--------------- Internals Below -------------------
		static Internal function create(def:ShapeDef/*NEVER USED , allocator:* */):Shape {
			switch (def.type) {
				case e_circleShape:
					{
					//void* mem = allocator->Allocate(sizeof(CircleShape));
					return new CircleShape(def);
					}
		
				case e_polygonShape:
					{
					//void* mem = allocator->Allocate(sizeof(PolygonShape));
					return new PolygonShape(def);
					}
		
				default:
					//Settings.Assert(false);
					return null;
			}
		}

		static Internal function destroy(shape:Shape/*NEVER USED , allocator:* */):void {
		/*switch (s.m_type)
		{
		case e_circleShape:
			//s->~Shape();
			//allocator->Free(s, sizeof(CircleShape));
			break;
		
		case e_polygonShape:
			//s->~Shape();
			//allocator->Free(s, sizeof(PolygonShape));
			break;
		
		default:
			//Settings.Assert(false);
		}*/
		}

		public function Shape(def:ShapeDef) {
		
			m_userData = def.userData;
			m_friction = def.friction;
			m_restitution = def.restitution;
			m_density = def.density;
			m_body = null;
			m_sweepRadius = 0.0;
		
			m_next = null;
		
			m_proxyId = Pair._nullProxy;
		
			m_filter = def.filter.copy;
		
			m_isSensor = def.isSensor;
		}

		//virtual ~Shape();

		//
		static private var s_proxyAABB:AABB = new AABB();

		Internal function createProxy(broadPhase:BroadPhase, transform:XForm):void {
		
			//Settings.Assert(m_proxyId == _nullProxy);
			var aabb:AABB = s_proxyAABB;
			computeAABB(aabb, transform);
		
			var inRange:Boolean = broadPhase.inRange(aabb);
		
			// You are creating a shape outside the world box.
			//Settings.Assert(inRange);
			if (inRange) {
				m_proxyId = broadPhase.createProxy(aabb, this);
			}
		else {
				m_proxyId = Pair._nullProxy;
			}
		}

		Internal function destroyProxy(broadPhase:BroadPhase):void {
		
			if (m_proxyId != Pair._nullProxy) {
				broadPhase.destroyProxy(m_proxyId);
				m_proxyId = Pair._nullProxy;
			}
		}

		//
		static private var s_syncAABB:AABB = new AABB();

		//
		Internal function synchronize(broadPhase:BroadPhase, transform1:XForm, transform2:XForm):Boolean {
		
			if (m_proxyId == Pair._nullProxy) {	
				return false;
			}
		
			// Compute an AABB that covers the swept shape (may miss some rotation effect).
			var aabb:AABB = s_syncAABB;
			computeSweptAABB(aabb, transform1, transform2);
		
			if (broadPhase.inRange(aabb)) {
				broadPhase.moveProxy(m_proxyId, aabb);
				return true;
			}
		else {
				return false;
			}
		}

		static private var s_resetAABB:AABB = new AABB();

		Internal function refilterProxy(broadPhase:BroadPhase, transform:XForm):void {
		
			if (m_proxyId == Pair._nullProxy) {
				return;
			}
		
			broadPhase.destroyProxy(m_proxyId);
		
			var aabb:AABB = s_resetAABB;
			computeAABB(aabb, transform);
		
			var inRange:Boolean = broadPhase.inRange(aabb);
		
			if (inRange) {
				m_proxyId = broadPhase.createProxy(aabb, this);
			}
		else {
				m_proxyId = Pair._nullProxy;
			}
		}

		Internal  function updateSweepRadius(center:Vec2):void {
		};

		Internal var m_type:int;
		Internal var m_next:Shape;
		Internal var m_body:Body;
		// Sweep radius relative to the parent body's center of mass.
		Internal var m_sweepRadius:Number;
		Internal var m_density:Number;
		Internal var m_friction:Number;
		Internal var m_restitution:Number;
		Internal var m_proxyId:uint;
		Internal var m_filter:FilterData;
		Internal var m_isSensor:Boolean;
		Internal var m_userData:*;
		/// The various collision shape types supported by Box2D.
		//enum ShapeType
		//{
		static Internal const e_unknownShape:int = -1;
		static Internal const e_circleShape:int = 0;
		static Internal const e_polygonShape:int = 1;
		static Internal const e_shapeTypeCount:int = 2;
	//};
	}
}
